  <!DOCTYPE html>
  <html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="generator" content="pandoc" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
    <title>GTK 4 tutorial</title>
    <style>
      code{white-space: pre-wrap;}
      span.smallcaps{font-variant: small-caps;}
      span.underline{text-decoration: underline;}
      div.column{display: inline-block; vertical-align: top; width: 50%;}
      div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
      ul.task-list{list-style: none;}
      pre{overflow: visible;}
      pre > code.sourceCode { white-space: pre; position: relative; }
      pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
      pre > code.sourceCode > span:empty { height: 1.2em; }
      code.sourceCode > span { color: inherit; text-decoration: inherit; }
      div.sourceCode { margin: 1em 0; }
      pre.sourceCode { margin: 0; }
      @media screen {
      div.sourceCode { overflow: auto; }
      }
      @media print {
      pre > code.sourceCode { white-space: pre-wrap; }
      pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
      }
      pre.numberSource code
        { counter-reset: source-line 0; }
      pre.numberSource code > span
        { position: relative; left: -4em; counter-increment: source-line; }
      pre.numberSource code > span > a:first-child::after
        { content: counter(source-line);
          position: relative; left: -1em; text-align: right; vertical-align: baseline;
          border: none; display: inline-block;
          -webkit-touch-callout: none; -webkit-user-select: none;
          -khtml-user-select: none; -moz-user-select: none;
          -ms-user-select: none; user-select: none;
          padding: 0 4px; width: 4em;
          color: #aaaaaa;
        }
      pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa;  padding-left: 4px; }
      div.sourceCode
        {   }
      @media screen {
      pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
      }
      code span.al { color: #ff0000; font-weight: bold; } /* Alert */
      code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
      code span.at { color: #7d9029; } /* Attribute */
      code span.bn { color: #40a070; } /* BaseN */
      code span.bu { } /* BuiltIn */
      code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
      code span.ch { color: #4070a0; } /* Char */
      code span.cn { color: #880000; } /* Constant */
      code span.co { color: #60a0b0; font-style: italic; } /* Comment */
      code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
      code span.do { color: #ba2121; font-style: italic; } /* Documentation */
      code span.dt { color: #902000; } /* DataType */
      code span.dv { color: #40a070; } /* DecVal */
      code span.er { color: #ff0000; font-weight: bold; } /* Error */
      code span.ex { } /* Extension */
      code span.fl { color: #40a070; } /* Float */
      code span.fu { color: #06287e; } /* Function */
      code span.im { } /* Import */
      code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
      code span.kw { color: #007020; font-weight: bold; } /* Keyword */
      code span.op { color: #666666; } /* Operator */
      code span.ot { color: #007020; } /* Other */
      code span.pp { color: #bc7a00; } /* Preprocessor */
      code span.sc { color: #4070a0; } /* SpecialChar */
      code span.ss { color: #bb6688; } /* SpecialString */
      code span.st { color: #4070a0; } /* String */
      code span.va { color: #19177c; } /* Variable */
      code span.vs { color: #4070a0; } /* VerbatimString */
      code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
      div.sourceCode { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
      pre:not(.sourceCode) { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
      table {margin-left: auto; margin-right: auto; border-collapse: collapse; border: 1px solid;}
      th {padding: 2px 6px; border: 1px solid; background-color: ghostwhite;}
      td {padding: 2px 6px; border: 1px solid;}
      img {display: block; margin-left: auto; margin-right: auto;}
      figcaption {text-align: center;}
    </style>
  </head>
  <body style="padding-top: 70px;">
    <div class="container">
    <nav class="navbar fixed-top navbar-expand-lg navbar-dark bg-primary">
      <div class="container-fluid">
        <span class="navbar-brand">Gtk4 tutorial</span>
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
          <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarSupportedContent">
          <ul class="navbar-nav me-auto mb-2 mb-lg-0">
            <li class="nav-item">
<a class="nav-link" href="index.html">Home</a>
</li>

            <li class="nav-item">
<a class="nav-link" href="sec20.html">Prev: section20</a>
</li>

            <li class="nav-item">
<a class="nav-link" href="sec22.html">Next: section22</a>
</li>

          </ul>
        </div>
      </div>
    </nav>
<h1 id="template-xml-and-composite-widget">Template XML and composite
widget</h1>
<p>The tfe program in the previous section is not so good because many
things are crammed into <code>tfepplication.c</code>. And many static
variables in <code>tfepplication.c</code>. The file
<code>tfeapplication.c</code> should be divided into several files.</p>
<ul>
<li><code>tfeapplication.c</code> only has codes related to the
application.</li>
<li>A file for the main window</li>
<li>A file for a preference dialog</li>
<li>A file for an alert dialog</li>
</ul>
<p>The preference dialog is defined by a ui file. And it has GtkBox,
GtkLabel and GtkFontButton in it. Such widget can be defined as a
composite widget. Composite widget is:</p>
<ul>
<li>a child object (not child widget) of a widget. For example, the
preference composite widget is a child object of GtkDialog.</li>
<li>Composite widget can be built from template XML. The widget is
defined with template tag, not object tag.</li>
</ul>
<p>Next subsection shows how to build a preference dialog.</p>
<h2 id="preference-dialog">Preference dialog</h2>
<p>First, write a template XML file.</p>
<p>@@<span class="citation" data-cites="include">@include</span>
tfe7/tfepref.ui @@@</p>
<p>Template tag specifies a composite widget. The value of a class
attribute is the object name. It is “TfePref”. A parent attribute
specifies the direct parent class of the composite widget. Therefore.
<code>TfePref</code> is a child class of <code>GtkDialog</code>. A
parent attribute is optional. But it is recommended to specify it. Other
lines are the same as before.</p>
<p>The class <code>TfePref</code> is defined like TfeTextView. There are
two files <code>tfepref.h</code> and <code>tfepref.c</code>.</p>
<p>The file <code>tfepref.h</code> defines types and declares public
functions. The definitions are public and open to any C files.</p>
<p>@@<span class="citation" data-cites="include">@include</span>
tfe7/tfepref.h @@@</p>
<ul>
<li>6: Defines a type <code>TFE_TYPE_PREF</code>, which is a macro
replaced by <code>tfe_pref_get_type ()</code>.</li>
<li>7: The macro <code>G_DECLAER_FINAL_TYPE</code> expands to:
<ul>
<li>The function <code>tfe_pref_get_type ()</code> is declared.</li>
<li>TfePrep type is defined as a typedef of
<code>struct _TfePrep</code>.</li>
<li>TfePrepClass type is defined as a typedef of
<code>struct {GtkDialogClass *parent;}</code>.</li>
<li>Two functions <code>TFE_PREF ()</code> and
<code>TFE_IS_PREF ()</code> is defined.</li>
</ul></li>
<li>9-10: <code>tfe_pref_new</code> creates a new TfePref object.</li>
</ul>
<p>The file <code>tfepref.c</code> includes:</p>
<ul>
<li><code>struct _TfePrep</code> structure</li>
<li><code>G_DEFINE_TYPE</code> macro</li>
<li>Initialize and dispose functions</li>
<li>public functions</li>
</ul>
<p>@@<span class="citation" data-cites="include">@include</span>
tfe7/tfepref.c @@@</p>
<ul>
<li>4-9: The structure <code>struct _TfePref</code> is defined. Every
TfePref instance has its own data of the structure. The structure has
references to:
<ul>
<li>a GSettings instance</li>
<li>a FontButton instance</li>
</ul></li>
<li>11: <code>G_DEFINE_TYPE</code> macro. The macro expands to:
<ul>
<li>the declaration of the class initialization function
<code>tfe_pref_class_init</code></li>
<li>the declaration of the instance initialization function
<code>tfe_pref_init</code></li>
<li>a static variable <code>tfe_pref_parent_class</code> that points the
parent class (GtkDialogClass) structure.</li>
<li>a definition of <code>tfe_pref_get_type ()</code> function</li>
</ul></li>
<li>13-19: <code>tfe_pref_dispose</code> function. It is called in the
destruction process and releases all the reference to other objects. For
further information about destruction process, refer to <a
href="sec11.html">Section 11</a>.</li>
<li>17: g_clear_object is often used in dispose handlers.
<code>g_clear_object (&amp;pref-&gt;gsettings)</code> does:
<ul>
<li><code>g_object_unref (pref-&gt;gsettings)</code></li>
<li><code>pref-&gt;settings =  NULL</code></li>
</ul></li>
<li>21-26: Instance initialization function. The argument
<code>pref</code> points a newly created TfePref instance.</li>
<li>23: The function <code>gtk_widget_init_template</code> creates and
initializes the child widgets. The widgets are created based on the
template which is created in the
<code>gtk_widget_class_set_template_from_resource</code> function.</li>
<li>24: Creates GSettings instance and assigns the pointer to it into
<code>pref-&gt;settings</code>. The instance refers to a GSetting id
<code>com.github.ToshioCP.tfe</code>.</li>
<li>25: Binds the GSettings data <code>font</code> and the
<code>font</code> property of <code>pref-&gt;fontbtn</code>
(GtkFontButton). The element <code>pref-&gt;fontbtn</code> points the
GtkFontButton, which is the instance of <code>fontbtn</code> in the ui
file. The relation was made by the
<code>gtk_widget_class_bind_template_child</code> function.</li>
<li>28-35: Class initialization function.</li>
<li>32: Sets the dispose handler.</li>
<li>33: <code>gtk_widget_class_set_template_from_resource</code>
function associates the description in the XML file
(<code>tfepref.ui</code>) with the widget. At this moment no instance is
created. It just makes the class recognize the structure of the object.
That’s why the top level tag is not <code>&lt;object&gt;</code> but
<code>&lt;template&gt;</code> in the XML file. The instance will be
created in the <code>gtk_widget_init_template</code> function
later.</li>
<li>34: <code>gtk_widget_class_bind_template_child</code> macro binds
the structure member (<code>fontbtn</code> in
<code>struct _TfePref</code>) and the id <code>fontbtn</code> in the XML
file. The two names must be the same. This binding is between the member
and the template (not an instance).</li>
<li>37-40: The function <code>tfe_pref_new</code> creates a TfePref
instance.</li>
</ul>
<p>Now, It is very simple to use this dialog. A caller just creates this
object and shows it.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>TfePref <span class="op">*</span>pref<span class="op">;</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>pref <span class="op">=</span> tfe_pref_new <span class="op">();</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>gtk_window_set_transient_for <span class="op">(</span>GTK_WINDOW <span class="op">(</span>pref<span class="op">),</span> win<span class="op">);</span> <span class="co">/* win is the main window */</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>gtk_window_present <span class="op">(</span>GTK_WINDOW <span class="op">(</span>pref<span class="op">));</span></span></code></pre></div>
<p>This instance is automatically destroyed when a user clicks on the
close button. That’s all. If you want to show the dialog again, just
create and show it.</p>
<figure>
<img src="image/pref_dialog.png" alt="Preference dialog" />
<figcaption aria-hidden="true">Preference dialog</figcaption>
</figure>
<h2 id="alert-dialog">Alert dialog</h2>
<p>It is almost same as preference dialog.</p>
<p>Its ui file is:</p>
<p>@@<span class="citation" data-cites="include">@include</span>
tfe7/tfealert.ui @@@</p>
<p>The header file is:</p>
<p>@@<span class="citation" data-cites="include">@include</span>
tfe7/tfealert.h @@@</p>
<p>There are three public functions. The functions
<code>tfe_alert_set_message</code> and
<code>tfe_alert_set_button_label</code> sets the label and button name
of the alert dialog. For example, if you want to show an alert that the
user tries to close without saving the content, set them like:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>tfe_alert_set_message <span class="op">(</span>alert<span class="op">,</span> <span class="st">&quot;Contents aren&#39;t saved yet.</span><span class="sc">\n</span><span class="st">Are you sure to close?&quot;</span><span class="op">);</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>tfe_alert_set_button_label <span class="op">(</span>alert<span class="op">,</span> <span class="st">&quot;Close&quot;</span><span class="op">);</span></span></code></pre></div>
<p>The function <code>tfe_alert_new</code> creates a TfeAlert
dialog.</p>
<figure>
<img src="image/alert_dialog.png" alt="Alert dialog" />
<figcaption aria-hidden="true">Alert dialog</figcaption>
</figure>
<p>The C source file is:</p>
<p>@@<span class="citation" data-cites="include">@include</span>
tfe7/tfealert.c @@@</p>
<p>The program is almost same as <code>tfepref.c</code>.</p>
<p>The Usage of the alert object is as follows.</p>
<ol type="1">
<li>Write the “response” signal handler.</li>
<li>Create a TfeAlert object.</li>
<li>Connect “response” signal to a handler</li>
<li>Show the dialog</li>
<li>In the signal handler, do something with regard to the response-id
and destroy the dialog.</li>
</ol>
<h2 id="top-level-window">Top-level window</h2>
<p><code>TfeWindow</code> is a child class of GtkApplicationWindow.</p>
<p>@@<span class="citation" data-cites="include">@include</span>
tfe7/tfewindow.ui @@@</p>
<p>This XML file is almost same as before except template tag and
“action-name” property in buttons.</p>
<p>GtkButton implements GtkActionable interface, which has “action-name”
property. If this property is set, GtkButton activates the action when
it is clicked. For example, if an open button is clicked, “win.open”
action will be activated and <code>open_activated</code> handler will be
invoked.</p>
<p>This action is also used by “&lt;Control&gt;o” accelerator (See
<code>tfeapplication.c</code>). If you used “clicked” signal for the
button, you would need its signal handler. Then, there would be two
handlers:</p>
<ul>
<li>a handler for the “clicked” signal on the button</li>
<li>a handler for the “activate” signal on the “win.open” action, to
which “&lt;Control&gt;o” accelerator is connected</li>
</ul>
<p>These two handlers are almost same. It is inefficient. Connecting
buttons to actions is a good way to reduce unnecessary codes.</p>
<p>@@<span class="citation" data-cites="include">@include</span>
tfe7/tfewindow.h @@@</p>
<p>There are three public functions. The function
<code>tfe_window_notebook_page_new</code> creates a new notebook page.
This is a wrapper function for <code>notebook_page_new</code>. It is
called by TfeApplication object. The function
<code>tfe_window_notebook_page_new_with_files</code> creates notebook
pages with a contents read from the given files. The function
<code>tfe_window_new</code> creates a TfeWindow instance.</p>
<p>@@<span class="citation" data-cites="include">@include</span>
tfe7/tfewindow.c @@@</p>
<ul>
<li>7-12: <code>_TfeWindow</code> structure. A TfeWindow instance points
the structure.</li>
<li>14: <code>G_DEFINE_TYPE</code> macro.</li>
<li>17-28: <code>alert_response_cb</code> is a call back function for
the “response” signal of TfeAlert dialog.</li>
<li>21: Destroys the alert dialog.</li>
<li>22-27: If the user has clicked on the accept button, it destroys the
main window or closes the current notebook page.</li>
<li>30-46: A “close-request” signal handler on the TfeWindow. When a
user clicked on the close button (top right x-shaped button), the
handler is called before the window closes. If the handler returns true,
the default handler isn’t called and the window doesn’t close. If the
handler returns false, the default handler is called and the window
closes.</li>
<li>34: If <code>has_saved_all</code> returns true, the handler returns
false and the window will close. Otherwise, it shows an alert
dialog.</li>
<li>48-111: Handlers of action activated signal. The
<code>user_data</code> is a pointer to the TfeWindow instance.</li>
<li>115-128: Public functions.</li>
<li>130-155: Instance initialization function.</li>
<li>135: The function <code>gtk_widget_init_template</code> creates a
child widgets and initializes them.</li>
<li>137-140: Builds and inserts <code>menu</code>. It is inserted to the
menu button.</li>
<li>143-152: Creates actions and inserts them to the window. The scope
of the actions is “win”.</li>
<li>154: Connects the “close-request” signal and a handler.</li>
<li>157-162: Class initialization function.</li>
<li>159: Sets the composite widget template</li>
<li>160-161: Binds private variables with child class templates.</li>
<li>164-167: <code>tfe_window_new</code>. This function creates
TfeWindow instance.</li>
</ul>
<h2 id="tfeapplication">TfeApplication</h2>
<p>The file <code>tfeaplication.h</code> and
<code>tfeapplication.c</code> are now very simple. The following is the
header file.</p>
<p>@@<span class="citation" data-cites="include">@include</span>
tfe7/tfeapplication.h @@@</p>
<ul>
<li>1: <code>#pragma once</code> isn’t an official pre-processor
command, but widely used. It makes the header file be read only
once.</li>
<li>5-6: <code>TFE_TYPE_APPLICATION</code> is defined as the type of
TfeApplication. <code>G_DECLARE_FINAL_TYPE</code> is a macro used in the
header file to define a new object.</li>
<li>8-9: The function <code>tfe_application_new</code> creates a new
TfeApplication instance.</li>
</ul>
<p>The following is <code>tfeapplication.c</code>. It defines the
application and supports:</p>
<ul>
<li>GSettings</li>
<li>CSS</li>
</ul>
<p>@@<span class="citation" data-cites="include">@include</span>
tfe7/tfeapplication.c @@@</p>
<ul>
<li>6-11: Defines <code>_TfeApplication</code> structure. The members
are:
<ul>
<li>win: main window instance</li>
<li>settings: GSettings instance.it is bound to “font” item in the
GSettings</li>
<li>provider: a provider for the font of the textview.</li>
</ul></li>
<li><code>G_DEFINE_TYPE</code> macro.</li>
<li>16-30: <code>changed_font_cb</code> is a handler for “changed::font”
signal on the GSettings instance. The signal name is “changed” and
“font” is a key name. When the valeu of “font” key is changed, the
signal is emitted. So, this handler doesn’t directly relate to the font
button, but through the GSettings database. A user changes the font in
the font button =&gt; GSettings font key data is changed =&gt; the
handler is called.</li>
<li>22-24: Retrieves a string from the GSetting database and converts it
into a pango font description.</li>
<li>25-29: Sets the css provider with the font data. The provider has
been inserted to the current display in advance.</li>
<li>33-39: Activate signal handler. It uses
<code>tfe_window_notebook_page_new</code> instead of
<code>notebook_page_new</code>.</li>
<li>41-47: Open signal handler. It just calls
<code>tfe_window_notebook_page_new_with_files</code> and shows the main
window. Be careful that the activate and open handlers don’t create a
new window. They just create a new notebook page. Therefore, even if the
second application runs, no new window appears. Just a new notebook page
is inserted to the same main window.</li>
<li>49-85: Startup signal handler.</li>
<li>56: Creates a new window (main window) and assigns it to
<code>app-&gt;win</code>.</li>
<li>57-61: Creates a css provider (<code>provider0</code>). It includes
only the padding data for the textview. The provider is inserted to the
default display.</li>
<li>63-65: Another css provider is created
(<code>app-&gt;provider</code>) and inserted to the default display. It
will include the font data for the textview.</li>
<li>66-68: Creates a new GSettings instance. If the GSettings data is
changed, the “changed” signal is emitted. The signal can have a key name
like “changed::font”. This style (“changed::font”) is called detailed
signal. The detailed signal is emitted only if the font data is changed.
The handler <code>changed_font_cb</code> is called to set the CSS with
the font data. The handler gets the font data from the GSettings data
which is the last font in the previous run of the application.</li>
<li>71-84: Defines accelerators.</li>
<li>87-94: A dispose handler. It releases references to the instances of
GSettings and GtkCssProvider.</li>
<li>96-101: An initialization for the instance. It connects three
signals (activate, open and startup) and their handlers.</li>
<li>183-188: An initialization for the class. It overrides the dispose
class method.</li>
<li>110-113: <code>tfe_application_new</code> creates a new
TfeApplication instance. The parameters are an application-id and
flags.</li>
</ul>
<h2 id="other-files">Other files</h2>
<p>main.c</p>
<p>@@<span class="citation" data-cites="include">@include</span>
tfe7/main.c @@@</p>
<p>CSS related files <code>pfd2css.h</code> and <code>pfd2css.c</code>
are the same as the previous section.</p>
<p>Resource XML file.</p>
<p>@@<span class="citation" data-cites="include">@include</span>
tfe7/tfe.gresource.xml @@@</p>
<p>GSchema XML file</p>
<p>@@<span class="citation" data-cites="include">@include</span>
tfe7/com.github.ToshioCP.tfe.gschema.xml @@@</p>
<p>Meson.build</p>
<p>@@<span class="citation" data-cites="include">@include</span>
tfe7/meson.build @@@</p>
<h2 id="compilation-and-installation.">Compilation and
installation.</h2>
<p>If you want to install it to your local area, use
<code>--prefix=$HOME/.local</code> or <code>--prefix=$HOME</code>
option. If you want to install it to the system area, no option is
needed. It will be installed under <code>/user/local</code>
directory.</p>
<pre><code>$ meson --prefix=$HOME/.local _build
$ ninja -C _build
$ ninja -C _build install</code></pre>
<p>You need root privilege to install it to the system area..</p>
<pre><code>$ meson _build
$ ninja -C _build
$ sudo ninja -C _build install</code></pre>
<p>Source files are in src/tfe7 directory.</p>
<p>Composite widgets give us two advantages.</p>
<ul>
<li>A set of widgets is better than individual widgets because of the
simple coding.</li>
<li>They hold instance variables (members of the object structure) so
static variables are no longer necessary. It makes the program
simpler.</li>
</ul>
<p>We made a very small text editor. You can add features to this
editor. When you add a new feature, be careful about the structure of
the program. Maybe you need to divide a file into several files like
this section. It isn’t good to put many things into one file. And it is
important to think about the relationship between source files and
widget structures.</p>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
  </body>
  </html>
